
#include <Archaism/String.h>
#include <algorithm>

_BeginNamespace(Radix)
/*
============================================================================================
==== StringUtil -- codec functions
============================================================================================
*/
/*
UCS-4 : 32b(31b used) 0xxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
byte[0] is named group (128 groups in total)
byte[1] is named plane (256 planes per group)
byte[2] is named row (256 rows per plane)
byte[3] is named cell (256 cells per row)
<group[0], plane[0]> is named Basic Multilingual Plane (BMP)
UCS-2 : 16b, equals UCS-4 BMP
*/
/*
Zero Width No-Break Space (BOM, Byte Order Mark)
Unicode BOM : FEFF
FE FF : Big Endian
FF FE : Little Endian
UTF8 BOM : EF BB BF
*/
/*
Unicode to UTF8
U-00000000 - U-0000007F: 0xxxxxxx
U-00000080 - U-000007FF: 110xxxxx 10xxxxxx
U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
*/
std::string StringUtil::unicodeToUtf8(const std::wstring& unicodeStr, bool bom)
{
	std::string utf8Str = "";
	if(bom)
	{
		// utf8 bom is EF BB BF
		char* bomStr = "\xef\xbb\xbf";
		utf8Str += bomStr;
	}

	const wchar_t* pos = unicodeStr.c_str();
	size_t length = unicodeStr.length();
	size_t count = 0;
	// check and ignore bom in unicodeStr
	if(length >= 1 && pos[0] == 0xFEFF)
	{
		count += 1;
		pos += 1;
	}
	while(count < length)
	{
		if(*pos < 0x80)
		{
			char c1 = *pos & 0x7f;
			utf8Str.push_back(c1);
		}
		else if(*pos < 0x800)
		{
			char c1 = ((*pos >> 6) & 0x1f) | 0xc0;
			char c2 = (*pos & 0x3f) | 0x80;
			utf8Str.push_back(c1);
			utf8Str.push_back(c2);
		}
		else if(*pos < 0x10000)
		{
			char c1 = ((*pos >> 12) & 0x0f) | 0xe0;
			char c2 = ((*pos >> 6) & 0x3f) | 0x80;
			char c3 = (*pos & 0x3f) | 0x80;
			utf8Str.push_back(c1);
			utf8Str.push_back(c2);
			utf8Str.push_back(c3);
		}
#if (_RADIX_UCS == _RADIX_UCS_4)
		else if(*pos < 0x200000)
		{
			char c1 = ((*pos >> 18) & 0x07) | 0xf0;
			char c2 = ((*pos >> 12) & 0x3f) | 0x80;
			char c3 = ((*pos >> 6) & 0x3f) | 0x80;
			char c4 = (*pos & 0x3f) | 0x80;
			utf8Str.push_back(c1);
			utf8Str.push_back(c2);
			utf8Str.push_back(c3);
			utf8Str.push_back(c4);
		}
		else if(*pos < 0x4000000)
		{
			char c1 = ((*pos >> 24) & 0x03) | 0xf8;
			char c2 = ((*pos >> 18) & 0x3f) | 0x80;
			char c3 = ((*pos >> 12) & 0x3f) | 0x80;
			char c4 = ((*pos >> 6) & 0x3f) | 0x80;
			char c5 = (*pos & 0x3f) | 0x80;
			utf8Str.push_back(c1);
			utf8Str.push_back(c2);
			utf8Str.push_back(c3);
			utf8Str.push_back(c4);
			utf8Str.push_back(c5);
		}
		else if(*pos < 0x80000000)
		{
			char c1 = ((*pos >> 30) & 0x01)|0xfc;
			char c2 = ((*pos >> 24) & 0x3f)|0x80;
			char c3 = ((*pos >> 18) & 0x3f)|0x80;
			char c4 = ((*pos >> 12) & 0x3f)|0x80;
			char c5 = ((*pos >> 6) & 0x3f)|0x80;
			char c6 = (*pos & 0x3f)|0x80;
			utf8Str.push_back(c1);
			utf8Str.push_back(c2);
			utf8Str.push_back(c3);
			utf8Str.push_back(c4);
			utf8Str.push_back(c5);
			utf8Str.push_back(c6);
		}
#endif//(_RADIX_UCS == _RADIX_UCS_4)

		count += 1;
		pos += 1;
	}

	return utf8Str;
}

std::wstring StringUtil::utf8ToUnicode(const std::string& utf8Str, bool bom)
{
	std::wstring unicodeStr = L"";
	if(bom)
	{
		unicodeStr.push_back(0xFEFF);
	}
	const char* pos = utf8Str.c_str();
	size_t length = utf8Str.length();
	size_t count = 0;
	// check and ignore bom in utf8Str
	if(length >= 3)
	{
		uchar c1 = pos[0];
		uchar c2 = pos[1];
		uchar c3 = pos[2];
		if(c1 == 0xEF && c2 == 0xBB && c3 == 0xBF)
		{
			count += 3;
			pos += 3;
		}
	}
	while(count < length)
	{
		wchar_t wc = L'\0';
		uchar c = *pos;
		if(c < 0xC0)
		{
			count += 1;
			if(count <= length)
			{
				wc = *pos;
				pos += 1;
			}
		}
		else if(c < 0xE0)
		{			
			count += 2;
			if(count <= length)
			{
				wc = (pos[0] & 0x1F) << 6;
				wc |= (pos[1] & 0x3F);
				pos += 2;
			}
		}
		else if(c < 0xF0)
		{
			count += 3;
			if(count <= length)
			{
				wc = (pos[0] & 0x0F) << 12;
				wc |= (pos[1] & 0x3F) << 6;
				wc |= (pos[2] & 0x3F);
				pos += 3;
			}
		}
#if (_RADIX_UCS == _RADIX_UCS_4)
		else if(c < 0xF8)
		{
			count += 4;
			if(count <= length)
			{
				wc = (*pos[0] & 0x07) << 18;
				wc |= (*pos[1] & 0x3F) << 12;
				wc |= (*pos[2] & 0x3F) << 6;
				wc |= (*pos[3] & 0x3F);
				pos += 4;
			}
		}
		else if(c < 0xFC)
		{
			count += 5;
			if(count <= length)
			{
				wc = (*pos[0] & 0x03) << 24;
				wc |= (*pos[1] & 0x3F) << 18;
				wc |= (*pos[2] & 0x3F) << 12;
				wc |= (*pos[3] & 0x3F) << 6;
				wc |= (*pos[4] & 0x3F);
				pos += 5;
			}
		}
		else
		{
			count += 6;
			if(count <= length)
			{
				wc = (*pos[0] & 0x01) << 30;
				wc |= (*pos[1] & 0x3F) << 24;
				wc |= (*pos[2] & 0x3F) << 18;
				wc |= (*pos[3] & 0x3F) << 12;
				wc |= (*pos[4] & 0x3F) << 6;
				wc |= (*pos[5] & 0x3F);
				pos += 6;
			}
		}
#endif//(_RADIX_UCS == _RADIX_UCS_4)
		unicodeStr.push_back(wc);
	}

	return unicodeStr;
}
/*
============================================================================================
==== StringUtil -- frequently used functions
============================================================================================
*/
String StringUtil::fromUtf8(const std::string& str)
{
#if(_RADIX_STRINGTYPE == _RADIX_STRINGTYPE_WCS)
	return StringUtil::utf8ToUnicode(str, false);
#else
	return str;
#endif
}

std::string StringUtil::toUtf8(const String& str)
{
#if(_RADIX_STRINGTYPE == _RADIX_STRINGTYPE_WCS)
	return StringUtil::unicodeToUtf8(str, false);
#else
	return str;
#endif
}

String StringUtil::fromUnicode(const std::wstring& str)
{
#if(_RADIX_STRINGTYPE == _RADIX_STRINGTYPE_MBS)
	return StringUtil::unicodeToUtf8(str, false);
#else
	return str;
#endif
}

std::wstring StringUtil::toUnicode(const String& str)
{
#if(_RADIX_STRINGTYPE == _RADIX_STRINGTYPE_MBS)
	return StringUtil::utf8ToUnicode(str, false);
#else
	return str;
#endif
}

bool StringUtil::contains(const String& str1, const String& str2)
{
	String::size_type len1 = str1.length();
	String::size_type len2 = str2.length();
	if (len1 < len2 || len2 == 0)
		return false;

	String::size_type index = str1.find(str2);
	if(index == String::npos)
		return false;
	
	return true;
}

bool StringUtil::containsOne(const String& str, const StringVector& strs)
{
	StringVector::const_iterator iter = strs.begin();
	while(iter != strs.end())
	{
		if(contains(str, *iter))
			return true;
		++ iter;
	}
	return false;
}

bool StringUtil::containsAll(const String& str, const StringVector& strs)
{
	StringVector::const_iterator iter = strs.begin();
	while(iter != strs.end())
	{
		if(contains(str, *iter))
			return false;
		++ iter;
	}
	return true;
}

bool StringUtil::startsWith(const String& str1, const String& str2)
{
	String::size_type len1 = str1.length();
	String::size_type len2 = str2.length();
	if (len1 < len2 || len2 == 0)
		return false;

	String startStr = str1.substr(0, len2);
	return (startStr == str2);
}

bool StringUtil::startsWith(const String& str, const StringVector& strs)
{
	StringVector::const_iterator iter = strs.begin();
	while(iter != strs.end())
	{
		if(startsWith(str, *iter))
			return true;
		++ iter;
	}
	return false;
}

bool StringUtil::endsWith(const String& str1, const String& str2)
{
	String::size_type len1 = str1.length();
	String::size_type len2 = str2.length();
	if (len1 < len2 || len2 == 0)
		return false;

	String endStr = str1.substr(len1 - len2, len2);
	return (endStr == str2);
}

bool StringUtil::endsWith(const String& str, const StringVector& strs)
{
	StringVector::const_iterator iter = strs.begin();
	while(iter != strs.end())
	{
		if(endsWith(str, *iter))
			return true;
		++ iter;
	}
	return false;
}

String StringUtil::toUpper(const String& str)
{
	String result(str);
	std::transform(result.begin(), result.end(), result.begin(), toupper);
	return result;
}

String StringUtil::toLower(const String& str)
{
	String result(str);
	std::transform(result.begin(), result.end(), result.begin(), tolower);
	return result;
}

String StringUtil::reverse(const String& str)
{
	String result(str);
	std::reverse(result.begin(), result.end());
	return result;
}

String StringUtil::substr(const String& str, String::size_type start, String::size_type len)
{
	return str.substr(start, len);
}

String StringUtil::left(const String& str, String::size_type len)
{
	return str.substr(0, len);
}

String StringUtil::right(const String& str, String::size_type len)
{
	String::size_type slen = str.length();
	if (slen <= len)
		return str;

	return str.substr(slen - len, len);
}

String StringUtil::replace(const String& str, const String& str1, const String& str2)
{
	String result(str);
	String::size_type index = result.find(str1);
	while(index != String::npos)   
	{
		result.replace(index, str1.length(), str2); 
		index = result.find(str1, index + str1.length());   
	}
	return result;
}

String StringUtil::replace(const String& str, const StringMap& nameValues)
{
	String result(str);
	StringMap::const_iterator iter = nameValues.begin();
	while(iter != nameValues.end())
	{
		result = replace(result, iter->first, iter->second);
		++ iter;
	}
	return result;
}

String StringUtil::trim(const String& str)
{
	static const String blank = _T(" \t\r");
	String result(str);
	result.erase(result.find_last_not_of(blank)+1);
	result.erase(0, result.find_first_not_of(blank));
	return result;
}

String StringUtil::trimLeft(const String& str)
{
	static const String blank = _T(" \t\r");
	String result(str);
	result.erase(0, result.find_first_not_of(blank));
	return result;
}

String StringUtil::trimRight(const String& str)
{
	static const String blank = _T(" \t\r");
	String result(str);
	result.erase(result.find_last_not_of(blank) + 1);
	return result;
}

StringVector StringUtil::split(const String& str, const String& delim)
{
	/* 
	StringVector result;
	String::size_type start = str.find_first_not_of(delim);
	while (start != String::npos)
	{
		String::size_type end = str.find_first_of(delim, start);
		if (end != String::npos)
		{
			result.push_back(str.substr(start, end - start));
		}
		else
		{
			result.push_back(str.substr(start));
			break;
		}
		start = str.find_first_not_of(delim, end + 1);
	}
	return result;*/
	// The algorithm above is very popular on the Internet that
	// each character in the delim string will be used by a delimiter,
	// but we need use the whole delim string as a delimiter. 
	StringVector result;
	String::size_type start = 0;
	while (start < str.length())
	{
		String::size_type end = str.find(delim, start);
		if(end == String::npos)
		{
			result.push_back(str.substr(start));
			break;
		}

		if(end > start)
		{
			result.push_back(str.substr(start, end - start));
		}
		start = end + delim.length();
	}
	return result;
}
/*
============================================================================================
==== StringUtil -- format functions
============================================================================================
*/
void StringUtil::format(String& result, const String::value_type* fmt, va_list ap)
{
#if(_RADIX_STRINGTYPE == _RADIX_STRINGTYPE_WCS)
	int size = _vsnwprintf(nullptr, 0, fmt, ap);
	String::value_type* buff = new String::value_type[size+1];
	_vsnwprintf(buff, size, fmt, ap);
	buff[size] = L'\0';
#else
	int size = vsnprintf(nullptr, 0, fmt, ap);
	String::value_type* buff = new String::value_type[size+1];
	vsnprintf(buff, size, fmt, ap);
	buff[size] = '\0';
#endif
	result = String(buff);
	delete []buff;
}

String StringUtil::format(const String::value_type* fmt, ...)
{
	String result;
	va_list ap = 0;
	va_start(ap, fmt);
	format(result, fmt, ap);
	va_end(ap);
	return result;
}
/*
============================================================================================
==== StringValue -- type casting class
============================================================================================
*/
StringValue::StringValue(const StringValue& var)
	:mValue(var.mValue)
{}

StringValue::StringValue(const String& value)
	:mValue(value)
{}

StringValue::StringValue(short value)
	:mValue(StringUtil::valueToString(value))
{}

StringValue::StringValue(ushort value)
	:mValue(StringUtil::valueToString(value))
{}

StringValue::StringValue(int value)
	:mValue(StringUtil::valueToString(value))
{}

StringValue::StringValue(uint value)
	:mValue(StringUtil::valueToString(value))
{}

StringValue::StringValue(long value)
	:mValue(StringUtil::valueToString(value))
{}

StringValue::StringValue(ulong value)
	:mValue(StringUtil::valueToString(value))
{}

StringValue::StringValue(longlong value)
	:mValue(StringUtil::valueToString(value))
{}

StringValue::StringValue(ulonglong value)
	:mValue(StringUtil::valueToString(value))
{}

StringValue::StringValue(float value)
	:mValue(StringUtil::valueToString(value))
{}

StringValue::StringValue(double value)
	:mValue(StringUtil::valueToString(value))
{}

StringValue::StringValue(longdouble value)
	:mValue(StringUtil::valueToString(value))
{}

StringValue::StringValue(bool value)
	:mValue(StringUtil::valueToString(value))
{}

StringValue& StringValue::operator=(const StringValue& var)
{
	mValue = var.mValue;
	return *this;
}

StringValue& StringValue::operator=(const String& value)
{
	mValue = value;
	return *this;
}

StringValue& StringValue::operator=(const String::value_type* value)
{
	mValue = value;
	return *this;
}

StringValue& StringValue::operator=(short value)
{
	mValue = StringUtil::valueToString(value);
	return *this;
}

StringValue& StringValue::operator=(ushort value)
{
	mValue = StringUtil::valueToString(value);
	return *this;
}

StringValue& StringValue::operator=(int value)
{
	mValue = StringUtil::valueToString(value);
	return *this;
}

StringValue& StringValue::operator=(uint value)
{
	mValue = StringUtil::valueToString(value);
	return *this;
}

StringValue& StringValue::operator=(long value)
{
	mValue = StringUtil::valueToString(value);
	return *this;
}

StringValue& StringValue::operator=(ulong value)
{
	mValue = StringUtil::valueToString(value);
	return *this;
}

StringValue& StringValue::operator=(longlong value)
{
	mValue = StringUtil::valueToString(value);
	return *this;
}

StringValue& StringValue::operator=(ulonglong value)
{
	mValue = StringUtil::valueToString(value);
	return *this;
}

StringValue& StringValue::operator=(float value)
{
	mValue = StringUtil::valueToString(value);
	return *this;
}

StringValue& StringValue::operator=(double value)
{
	mValue = StringUtil::valueToString(value);
	return *this;
}

StringValue& StringValue::operator=(longdouble value)
{
	mValue = StringUtil::valueToString(value);
	return *this;
}

StringValue& StringValue::operator=(bool value)
{
	mValue = StringUtil::valueToString(value);
	return *this;
}

StringValue::operator const String&() const
{
	return mValue;
}

StringValue::operator short() const
{
	return StringUtil::stringToValue<short>(mValue);
}

StringValue::operator ushort() const
{
	return StringUtil::stringToValue<ushort>(mValue);
}

StringValue::operator int() const
{
	return StringUtil::stringToValue<int>(mValue);
}

StringValue::operator uint() const
{
	return StringUtil::stringToValue<uint>(mValue);
}

StringValue::operator long() const
{
	return StringUtil::stringToValue<long>(mValue);
}

StringValue::operator ulong() const
{
	return StringUtil::stringToValue<ulong>(mValue);
}

StringValue::operator longlong() const
{
	return StringUtil::stringToValue<longlong>(mValue);
}

StringValue::operator ulonglong() const
{
	return StringUtil::stringToValue<ulonglong>(mValue);
}

StringValue::operator float() const
{
	return StringUtil::stringToValue<float>(mValue);
}

StringValue::operator double() const
{
	return StringUtil::stringToValue<double>(mValue);
}

StringValue::operator longdouble() const
{
	return StringUtil::stringToValue<longdouble>(mValue);
}

StringValue::operator bool() const
{
	return StringUtil::stringToValue<bool>(mValue);
}

_EndNamespace(Radix)
